package com.hb.config;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.cache.ehcache.EhCacheCacheManager;
import org.springframework.cache.ehcache.EhCacheManagerFactoryBean;
import org.springframework.cache.jcache.JCacheCacheManager;
import org.springframework.cache.support.CompositeCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;

import java.util.ArrayList;
import java.util.List;

@Configuration
// 启用注解驱动的缓存（启用 Spring 对注解驱动缓存的支持）
@EnableCaching
public class CachingConfig {
    // ===================================================声明内存缓存管理器==============================================
    /**
     * 声明缓存管理器
     * 缓存管理器是 Spring 缓存抽象的核心，它能够与多个流行的缓存实现进行集成。
     * 在本例中，声明了 ConcurrentMapCacheManager，这个简单的缓存管理器使用 java.util.concurrent.ConcurrentHashMap 作为其缓存存储。
     * 它非常简单，因此对于开发、测试或基础的应用来讲，这是一个很不错的选择。但它的缓存存储是基于内存的，所以它的生命周期是与应用关联的，对于生产
     * 级别的大型企业级应用程序，这可能并不是理想的选择。
     * @return
     */
    @Bean
    public CacheManager cacheManager() {
        return new ConcurrentMapCacheManager();
    }

    // ===================================================声明 EhCache 缓存管理器==============================================

    /**
     * 配置 EhCacheManagerFactoryBean
     * 我们需要使用 EhCache 的 CacheManager 来进行注入，所以必须也要声明一个 CacheManager bean。为了对其进行简化，Spring 提供了 EhCacheManagerFactoryBean
     * 来生成 EhCache 的 CacheManager。因为它是一个工厂 bean（也就是说，它实现了 Spring 的 FactoryBean 接口），所以注册在 Spring 应用上下文中的并不是
     * EhCacheManagerFactoryBean 的实例，而是 CacheManager（EhCache 的 CacheManager） 的一个实例，因此适合注入到 EhCacheCacheManager 之中。
     * @return
     */
    @Bean
    public EhCacheManagerFactoryBean ehCacheManagerFactoryBean() {
        EhCacheManagerFactoryBean ehCacheManagerFactoryBean = new EhCacheManagerFactoryBean();
        /**
         * 除了在 Spring 中配置的 bean，还需要有针对 EhCache 的配置。EhCache 为 XML 定义了自己的配置模式，我们需要在一个 XML 文件中配置缓存，该文件需要符合
         * EhCache 所定义的模式。在创建 EhCacheManagerFactoryBean 的过程中，需要告诉它 EhCache 配置文件在什么地方。在这里通过调用 setConfigLocation() 方
         * 法，传入 ClassPathResource，用来指明 EhCache XML 配置文件相对于根类路径（classpath）的位置。
         */
        ehCacheManagerFactoryBean.setConfigLocation(new ClassPathResource("cache/ehcache.xml"));
        return ehCacheManagerFactoryBean;
    }

    /**
     * 配置 EhCacheCacheManager
     * 在这里，稍微有点诡异的注入可能会让人感觉迷惑，这是因为 Spring 和 EhCache 都定义了 CacheManager 类型。需要明确的是，EhCache 的 CacheManager 要被注入到
     * Spring 的 EhCacheCacheManager（Spring CacheManager 的实现）之中。
     * @param cm
     * @return
     */
    @Bean
    public EhCacheCacheManager ehCacheCacheManager(net.sf.ehcache.CacheManager cm) {
        return new EhCacheCacheManager(cm);
    }

    // ===================================================声明 Redis 缓存管理器==============================================

    /**
     * Redis 连接工厂 bean
     * @return
     */
    @Bean
    public JedisConnectionFactory redisConnectionFactory() {
        JedisConnectionFactory jedisConnectionFactory = new JedisConnectionFactory();
        jedisConnectionFactory.afterPropertiesSet();
        return jedisConnectionFactory;
    }

    /**
     * RedisTemplate bean
     * @param redisCF
     * @return
     */
    @Bean
    public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory redisCF) {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisCF);
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    /**
     * Redis 缓存管理器 bean
     * @param redisTemplate
     * @return
     */
    @Bean
    public CacheManager cacheManager1(RedisTemplate redisTemplate) {
        return new RedisCacheManager(redisTemplate);
    }

    // ===================================================声明 CompositeCacheManager ==============================================

    /**
     * 配置 CompositeCacheManager bean
     * 当查找缓存条目时，CompositeCacheManager 首先会从 JCacheCacheManager 开始检查 JCache 实现，然后通过 EhCacheCacheManager 检查 Ehcache，最后会使
     * 用 RedisCacheManager 来检查 Redis，完成缓存条目的查找。
     * @param cm
     * @param jcm
     * @return
     */
    @Bean
    public CacheManager cacheManager2(net.sf.ehcache.CacheManager cm, javax.cache.CacheManager jcm) {
        CompositeCacheManager cacheManager = new CompositeCacheManager();
        List<CacheManager> managers = new ArrayList<CacheManager>();
        managers.add(new JCacheCacheManager(jcm));
        managers.add(new EhCacheCacheManager(cm));
        managers.add(new RedisCacheManager(redisTemplate()));
        cacheManager.setCacheManagers(managers);
        return cacheManager;
    }
}
